package fr.ece.ing4.si.multithreadedmontecarlo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Random;
import java.io.IOException;
import java.util.concurrent.ExecutionException;


public class MultiThreadedMonteCarloSimulation {
private int nbThreads = 4;
private String CallPutFlag = "c"; // The option is a call
private int S = 40; // Underlying asset actual price
private int X = 50; // Strike price
private double r = 0.06; // Basic interest rate (6%)
private double T = 0.5; // Time remaining until the maturity of the option -> 6 months
private double b = 0.10; // Detention costs rate of the option (10%)
private double v = 0.45; // Volatility (45%)
private int nSteps = 168;
private int nSimulations = 100000;
public double computeResult(String CallPutFlag, int S, int X, double r, double T, double b, double v, final int nSteps, int nSimulations) throws InterruptedException, Exception{

double dt = 0;
double St = 0;
double Sum = 0;
double optionPrice = 0;
int z = 0;
final Random rand = new Random();
dt = T / nSteps;
double Drift = (b - Math.pow(v,2) / 2) * dt;;
double vSqrdt = v * Math.sqrt(dt);
if(CallPutFlag.equals("c")) z = 1;
else if(CallPutFlag.equals("p")) z = -1;
// create Callable to compute calculus
List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
for(int i=0 ; i<nSimulations ; i++) {
 final double localSt = S;
 final double localDrift = Drift;
 final double localvSqrdt = vSqrdt;
 
     partitions.add(new Callable<Double>() {
       public Double call() throws Exception {
    	final Random localRand = new Random();
       	double ite = localSt;
       	for(int j=0 ; j<nSteps ; j++) 
       	ite *= Math.exp(localDrift + localvSqrdt * localRand.nextGaussian());
       	return ite;
       }
     });
}

ExecutorService executorPool = Executors.newFixedThreadPool(nbThreads);    
   List<Future<Double>> valueOfComputations = executorPool.invokeAll(
   	partitions, 10000, TimeUnit.SECONDS);
   executorPool.shutdown();
     
   double netAssetValue = 0.0;
   for(Future<Double> valueOfAComputations : valueOfComputations) {
   	Sum += Math.max(z * (valueOfAComputations.get() - X), 0);
   	
   }
   optionPrice = Math.exp(-r * T) * (Sum / nSimulations);
   
System.out.println("dt = " + dt);
System.out.println("St = " + St);
System.out.println("Sum = " + Sum);
System.out.println("Drift = " + Drift);
System.out.println("vSqrdt = " + vSqrdt);
System.out.println("optionPrice = " + optionPrice);
System.out.println("z = " + z);
System.out.println("canarie");
return optionPrice;
}

}